package org.budo.cmbc.direct.sdk.util.ecp;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.List;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.budo.cmbc.direct.sdk.entity.ecp.ClientConfig;
import org.budo.cmbc.direct.sdk.entity.ecp.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 通过表单的方式发送http请求服务
 *
 */
public class CmbcHttpUtil {
    private static final Logger log = LoggerFactory.getLogger(CmbcHttpUtil.class);

    /**
     * 初始化状态
     */
    private static boolean sslInited = false;

    private static SSLConnectionSocketFactory sslsf;

    private static String socketTimeout = "60000";
    private static String connectTimeout = "60000";
    private static String connectionRequestTimeout = "60000";

    private static HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };

    @SuppressWarnings("resource")
    private static void initSSL(String clientKeyStoryPath, String clientKeyStoryPassword) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException {
        // 构造SSL环境
        SSLContext sslContext = SSLContext.getInstance("SSL");

        // keyStore类型，JKS、pkcs12
        KeyStore keyStore = KeyStore.getInstance("pkcs12");

        // 创建jks密钥访问库
        if (null != clientKeyStoryPassword && null != clientKeyStoryPath && clientKeyStoryPassword.length() > 0 && clientKeyStoryPath.length() > 0) {
            keyStore.load(new FileInputStream(clientKeyStoryPath), clientKeyStoryPassword.toCharArray());
        } else {
            throw new RuntimeException("SSL认证证书路径或密码为空。");
        }

        // 创建管理jks密钥库的x509密钥管理器，用来管理密钥，需要key的密码
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, clientKeyStoryPassword.toCharArray());

        // 只允许使用TLSv1协议
        sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
        sslsf = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1" }, null, ignoreHostnameVerifier);
    }

    protected SSLContext buildSSLContext() {
        SSLContext sslContext = null;
        try {
            sslContext = SSLContextBuilder.create().build();
        } catch (KeyManagementException e1) {
            log.error("SSLContextBuilder构建SSLContext发生KeyManagementException", e1);
            throw new RuntimeException("SSLContextBuilder构建SSLContext发生KeyManagementException", e1);
        } catch (NoSuchAlgorithmException e1) {
            log.error("SSLContextBuilder构建SSLContext发生NoSuchAlgorithmException", e1);
            throw new RuntimeException("SSLContextBuilder构建SSLContext发生NoSuchAlgorithmException", e1);
        }

        return sslContext;
    }

    /**
     * 发送post请求
     *
     * @param url
     * @param list
     * @return
     * @throws Exception
     * @function
     */
    public static String doPost(String url, List<NameValuePair> list, ClientConfig clientConfig) throws Exception {
        String result = null;
        CloseableHttpClient httpclient = null;
        // 是否使用https
        boolean isSSL = clientConfig.getIsSSL();
        if (isSSL) {
            if (!sslInited) {
                try {
                    initSSL(clientConfig.getSslClientCertPath(), clientConfig.getSslClientPassword());
                } catch (Exception e) {
                    String errMsg = "初始化SSL失败";
                    log.error(errMsg, e);
                    throw new Exception(errMsg, e);
                }
            }
            httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } else {
            httpclient = HttpClients.custom().build();
        }

        HttpPost httpPost = new HttpPost(url);
        RequestConfig config = RequestConfig.custom().setSocketTimeout(Integer.parseInt(socketTimeout)).setConnectTimeout(Integer.parseInt(connectTimeout)).setConnectionRequestTimeout(Integer.parseInt(connectionRequestTimeout)).build();
        httpPost.setConfig(config);
        try {
            UrlEncodedFormEntity reqEntity = new UrlEncodedFormEntity(list, Constants.DEFAULT_CHARSET);
            httpPost.setEntity(reqEntity);

            CloseableHttpResponse response = httpclient.execute(httpPost);

            if (response == null || response.getStatusLine().getStatusCode() != 200) {
                String errMsg = "发送HTTP请求后返回报文为空或HTTP状态码不为200(" + (response == null ? "返回报文为空" : response.getStatusLine().getStatusCode()) + ")";
                log.error(errMsg);
                throw new Exception(errMsg);
            }
            try {
                HttpEntity respEntity = response.getEntity();

                if (respEntity != null) {
                    result = EntityUtils.toString(respEntity, Constants.DEFAULT_CHARSET);
                }
                EntityUtils.consume(respEntity);
            } catch (IOException ex) {
                String errMsg = "Post响应解析失败";
                log.error(errMsg, ex);
                throw new Exception(errMsg, ex);
            } finally {
                response.close();
            }
        } catch (Exception ex) {
            String errMsg = "Post请求失败";
            log.error(errMsg, ex);
            throw new Exception(errMsg, ex);
        } finally {
            httpclient.close();
        }
        return result;
    }
}
